{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c36d95bb",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "#  18\\.  支持向量机实现人像分类  # \n",
    "\n",
    "##  18.1.  介绍  # \n",
    "\n",
    "支持向量机是一个非常优秀的算法。本次挑战中，我们将使用 scikit-learn 提供的支持向量机方法来完成人脸图像分类任务。 \n",
    "\n",
    "##  18.2.  知识点  # \n",
    "\n",
    "  * 图像数据预处理 \n",
    "\n",
    "  * 支持向量机分类 \n",
    "\n",
    "首先，我们通过 scikit-learn 提供的 ` fetch_lfw_people  ` 下载人脸数据集，数据集最初出自 [ Labeled Faces in the Wild ](http://vis-www.cs.umass.edu/lfw/) 项目。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d720bdf2",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "outputs": [],
   "source": [
    "from sklearn.datasets import fetch_lfw_people\n",
    "\n",
    "# 加载数据集\n",
    "faces = fetch_lfw_people(min_faces_per_person=60)\n",
    "faces.target_names, faces.images.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e9a3fa7e",
   "metadata": {},
   "outputs": [],
   "source": [
    "(array(['Ariel Sharon', 'Colin Powell', 'Donald Rumsfeld', 'George W Bush',\n",
    "        'Gerhard Schroeder', 'Hugo Chavez', 'Junichiro Koizumi',\n",
    "        'Tony Blair'], dtype='<U17'),\n",
    " (1348, 62, 47))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78543185",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "可以看的，我们仅使用了 8 位名人的人像数据，总共包含 1348 个样本。其中，每张人像照片的尺寸为  $62*47$  像素。总结 ` faces  ` 属性有： \n",
    "\n",
    "属性  |  描述   \n",
    "---|---  \n",
    "` faces.images  ` |  62x47 矩阵，记录人脸图像中的像素值   \n",
    "` faces.data  ` |  将 images 对应的 62x47 矩阵转换为行向量   \n",
    "` faces.target_names  ` |  8 位人像姓名   \n",
    "` faces.target  ` |  8 位人像依次编号   \n",
    "  \n",
    "下面，我们先使用 Matplotlib 绘图来预览这些数据。 \n",
    "\n",
    "Exercise 18.1 \n",
    "\n",
    "挑战：预览数据集前 5 张人像图片，以 1 行 5 列子图呈现。 \n",
    "\n",
    "规定：每张图片横轴上显示该张图片对应人像姓名。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7acabbe8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib import pyplot as plt\n",
    "\n",
    "%matplotlib inline\n",
    "\n",
    "## 代码开始 ### (≈4 行代码)\n",
    "\n",
    "## 代码结束 ###"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96b93f90",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "参考答案  Exercise 18.1 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2336bb0f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib import pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "### 代码开始 ### (≈4 行代码)\n",
    "fig, axes = plt.subplots(1, 5, figsize=(12, 6))\n",
    "for i, image in enumerate(faces.images[:5]):\n",
    "    axes[i].imshow(image)\n",
    "    axes[i].set_xlabel(faces.target_names[faces.target[i]])\n",
    "### 代码结束 ###"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc4cf2a5",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "期望输出 \n",
    "\n",
    "![image](https://cdn.aibydoing.com/aibydoing/images/document-uid214893labid7506timestamp1547025771472.png)\n",
    "\n",
    "由于图片本身为 2 维数组，需要处理之后才能用于训练模型。所以，下面我们使用 ` faces.data  ` 数据，其已经将每一个人像对于的二维数组展平成 1 维。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0a54917",
   "metadata": {},
   "outputs": [],
   "source": [
    "faces.data.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b45474c9",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "可以看的， ` faces.data  ` 的形状为  $(1348, 2914)$  ，即代表有 1348 个样本，每一个样本对应了 2914 个特征。而这 2914 个特征即是将人像图片  $62*47 = 2914$  展平之后的向量。 \n",
    "\n",
    "接下来，按照惯例需要对数据集进行切分，将其分为训练集和测试集。不过，这里值的注意的是，由于样本只有 1348 个，而每个样本对应的特征则为 2914。机器学习建模过程中，我们要避免特征远大于样本数量的情形，这样训练出来的模型一般表现都会非常糟糕。 \n",
    "\n",
    "所以，这里我们需要对数据特征进行「降维」，实际上就是减少数据的特征量。这里使用到 PCA 降维方法，该方法会在后续实验中详细介绍，这里不做讲解。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f5c709ec",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.decomposition import PCA\n",
    "\n",
    "# 直接运行，将数据特征缩减为 150 个\n",
    "pca = PCA(n_components=150, whiten=True, random_state=42)\n",
    "pca_data = pca.fit_transform(faces.data)\n",
    "pca_data.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63c93430",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "可以看的，数据形状又先前的  $(1348, 2914)$  变为  $(1348, 150)$  。 \n",
    "\n",
    "下面，就可以根据降维后的数据切分训练集和测试集了。 \n",
    "\n",
    "Exercise 18.2 \n",
    "\n",
    "挑战：使用 ` train_test_split()  ` 将数据集切分为 80%（训练集） 和 20%（测试集） 两部分。 \n",
    "\n",
    "规定：训练集特征，测试集特征，训练集目标，测试集目标分别为： ` X_train  ` , ` X_test  ` , ` y_train  ` , ` y_test  ` ，随机数种子定为 42。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ede01cd8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "## 代码开始 ### (≈1 行代码)\n",
    "\n",
    "## 代码结束 ###\n",
    "\n",
    "X_train.shape, X_test.shape, y_train.shape, y_test.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df69b4cd",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "参考答案  Exercise 18.2 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5867215e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "### 代码开始 ### (≈1 行代码)\n",
    "X_train, X_test, y_train, y_test = train_test_split(\n",
    "    pca_data, faces.target, test_size=0.2, random_state=42)\n",
    "### 代码结束 ###\n",
    "\n",
    "X_train.shape, X_test.shape, y_train.shape, y_test.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "284384a3",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "期望输出 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f60acf6",
   "metadata": {},
   "outputs": [],
   "source": [
    "((1078, 150), (270, 150), (1078,), (270,))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15ec5b7a",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "接下来，我们使用 SVM 算法建模，并使用上面划分的数据训练和测试模型。 \n",
    "\n",
    "Exercise 18.3 \n",
    "\n",
    "挑战：使用 scikit-learn 提供的支持向量机分类方法完成建模，并得到模型在测试集上的准确度结果。 \n",
    "\n",
    "规定：支持向量机分类器参数 ` C=10  ` , ` gamma=0.001  ` ，其余为默认参数。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21b65d33",
   "metadata": {},
   "outputs": [],
   "source": [
    "## 代码开始 ### (≈4 行代码)\n",
    "model = None\n",
    "## 代码结束 ###"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d403ed8a",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "参考答案  Exercise 18.3 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73e18928",
   "metadata": {},
   "outputs": [],
   "source": [
    "### 代码开始 ### (≈4 行代码)\n",
    "from sklearn.svm import SVC\n",
    "\n",
    "model = SVC(C=10, gamma=0.001)\n",
    "model.fit(X_train, y_train)\n",
    "model.score(X_test, y_test)\n",
    "### 代码结束 ###"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15a5b6ba",
   "metadata": {},
   "source": [
    "期望输出 \n",
    "\n",
    "最终准确度  $>0.8$  即可。 "
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "main_language": "python",
   "notebook_metadata_filter": "-all"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
